package com.googlecode.tidej.client;

import java.io.IOException;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.googlecode.tidej.client.editor.ArtifactEditor;
import com.googlecode.tidej.client.project.ConnectionDialog;
import com.googlecode.tidej.client.project.ProjectMetaData;
import com.googlecode.tidej.client.project.ProjectManager;
import com.googlecode.tidej.client.tree.ClassTree;
import com.googlecode.tidej.client.util.DataSource;
import com.googlecode.tidej.shared.idl.IdlLoader;
import com.googlecode.tidej.shared.model.Artifact;
import com.googlecode.tidej.shared.model.Library;
import com.googlecode.tidej.shared.model.Model;
import com.googlecode.tidej.shared.model.Operation;
import com.googlecode.tidej.shared.model.Property;
import com.googlecode.tidej.shared.model.Type;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Tidej implements TidejController, EntryPoint {
  Model model = new Model();
  ArtifactEditor artifactEditor;
  ClassTree classTree;
  ProjectManager projectManager;
  
  /**
   * This is the entry point method.
   */
  @Override
  public void onModuleLoad() {
    new IdlLoader(model, new SimpleAsyncCallback<Void>() {
      @Override
      public void onSuccess(Void result) {
        showIde();
      }
    }).run();
  }
  
  public void showIde() {
    classTree = new ClassTree(this);
    
//    DockLayoutPanel leftPanel = new DockLayoutPanel(Unit.PX);
//   leftPanel.add(classTree);
    
    SplitLayoutPanel mainPanel = new SplitLayoutPanel(20);
    mainPanel.addWest(classTree, 200);
    
    
    artifactEditor = new ArtifactEditor(this);
    mainPanel.add(artifactEditor);

    RootLayoutPanel.get().add(mainPanel);

    History.addValueChangeHandler(new ValueChangeHandler<String>() {
      @Override
      public void onValueChange(ValueChangeEvent<String> event) {
        gotoHistoryToken(event.getValue());
      }
    });

    final String token = History.getToken();
    if (token == null || !token.startsWith("Project/")) {
      String lastProject = PersistentState.load().getLastOpened();
      if (lastProject != null && lastProject.length() > 0) {
        openProject(lastProject, new SimpleAsyncCallback<Void>() {
          @Override
          public void onSuccess(Void result) {
            gotoHistoryToken(token);
          }
        });
      }
    } else {
      gotoHistoryToken(token);
    }
  }

  @Override
  public void select(Artifact a) {
    classTree.select(a);
    // Perform our own selection here: classTree may not fire an event
    // if the selection does not change.
    selected(a);
  }
  
  @Override
  public void selected(Artifact artifact) {
    String token = artifact == null ? "" : ((artifact.getLibrary() == getProject() ? "Project/" : "Library/") + artifact.getQualifiedName());
    if (!token.equals(History.getToken())) {
      History.newItem(token, false);
    }
    artifactEditor.setArtifact(artifact, true);
  }

  @Override
  public Model getModel() {
    return model;
  }


  @Override
  public void saveCode(Type type) {
    saveCode();
  }

  public void upload() {
    if (projectManager != null) {
      new ConnectionDialog(this, ProjectMetaData.TargetType.DEPLOYMENT).run();
    }
  }

  @Override
  public void saveCode() {
    if (projectManager != null) {
      projectManager.saveCode();
    }
  }

  public void refreshSubtree(Artifact parent) {
    classTree.refresh(parent);
  }
  
  @Override
  public void closeProject(boolean save) {
    artifactEditor.setArtifact(null, save);
    model.removeLibrary(getProject());
    projectManager = null;
    classTree.refresh();
    
    PersistentState projectList = PersistentState.load();
    projectList.setLastOpened("");
    try {
      projectList.save();
      if (History.getToken().startsWith("Project/")) {
        History.newItem("", false);
      }
    } catch(IOException e) {
      Window.alert("Local Storage Problem: " + e);
    }
  }


  @Override
  public void deleteProject() {
    ProjectManager manager = getProjectManager();
    closeProject(true);
    manager.delete();
  }

  public void localStorageError(IOException e) {
    Window.alert("Local Storage Error: " + e.getMessage());
  }
  
  @Override
  public void deleteType(Type type) {
    Library library = type.getLibrary();
    library.deleteType(type);
    select(library);
    refreshSubtree(library);
    saveCode();
  }

  private void gotoHistoryToken(Library lib, String remainder) {
    if (lib == null) {
      return;
    }
    String[] parts = remainder.split("\\.");
    if (parts.length > 0) {
      Type type = lib.getType(parts[0]);
      if (type != null) {
        if (parts.length > 1) {
          Property property = type.getProperty(parts[1]);
          if (property != null) {
            select(property);
            return;
          }
          Operation operation = type.getOperation(parts[1]);
          if (operation != null) {
            select(operation);
            return;
          }
        }
        select(type);
        return;
      }
    }
    select(lib);
  }
  
  private void gotoHistoryToken(String token) {
    String[] mainParts = token.split("/");
    if (mainParts.length < 2) {
      select(getProject());
      return;
    }
    String libName = mainParts[1].replace('+', ' ');
    final String part3 = mainParts.length > 2 ? mainParts[2] : "";
    if (mainParts[0].equals("Project")) {
      if (ProjectManager.exists(libName)) {
        openProject(libName, new SimpleAsyncCallback<Void>() {
          @Override
          public void onSuccess(Void result) {
            gotoHistoryToken(getProject(), part3);
          }
        });
      }
    } else {
      gotoHistoryToken(model.getLibrary(libName), part3);
    }
  }

  @Override
  public void deleteOperation(Operation operation) {
    Type owner = operation.getOwner();
    owner.removeOperation(operation);
    saveCode(owner);
    select(owner);
    refreshSubtree(owner);
  }

  @Override
  public void deleteProperty(Property property) {
    Type owner = property.getOwner();
    owner.removeProperty(property);
    saveCode(owner);
    select(owner);
    refreshSubtree(owner);
  }

  private MultiWordSuggestOracle cache;
  
  @Override
  public SuggestOracle getTypeSuggestOracle() {
    if (cache == null) {
     cache = new MultiWordSuggestOracle();
     cache.addAll(model.getAllTypeNames());
    }
    return cache;
  }

  @Override
  public ProjectManager getProjectManager() {
    return projectManager;
  }

  @Override
  public Library getProject() {
    return projectManager == null ? null : projectManager.getProjectLibrary();
  }

  @Override
  public void connect() {
    new ConnectionDialog(this, ProjectMetaData.TargetType.DEVELOPMENT).run();
  }

  @Override
  public void setOnlineState(String url, boolean connected) {
    artifactEditor.setOnlineState(url, connected);
  }


  @Override
  public void createProject(ProjectMetaData metaData, final DataSource fromSample) {
    final ProjectManager newProjectManager = new ProjectManager(this, metaData.getName());
    newProjectManager.create(metaData, fromSample, new SimpleAsyncCallback<Void>() {
      @Override
      public void onSuccess(Void result) {
        projectManager = newProjectManager;
        classTree.refresh();
        if (fromSample != null) {
          saveCode();
        }
        classTree.select(getProject().getGlobals().getConstructor());
      }
    });
  }

  @Override
  public void openProject(String name, final AsyncCallback<Void> callback) {
    if (getProject() != null && getProject().getName().equals(name)) {
      if (callback != null) {
        callback.onSuccess(null);
      } else {
        classTree.select(getProject().getGlobals().getConstructor());
      }
      return;
    } 
    final ProjectManager newProjectManager = new ProjectManager(this, name);
    newProjectManager.open(new SimpleAsyncCallback<Void>() {
      @Override
      public void onSuccess(Void result) {
        projectManager = newProjectManager;
        classTree.refresh();
        if (callback != null) {
          callback.onSuccess(null);
        } else {
          classTree.select(getProject().getGlobals().getConstructor());
        }
      }
    });
  }

}
